/**
 * 
 */
package com.google.code.netz.wfe.model;

import java.util.ArrayList;
import java.util.List;

import com.google.code.netz.wfe.api.WorkflowException;

/**
 * A Transition is synonymous with a Task.
 * <p>
 * A Transition <code><b>'t'</b></code> is said to be enabled if and only if
 * each input {@link Place} of 't' contains at least one token.
 * <p>
 * A Transition can therefore be defined by the number of {@link Place}s it is
 * composed of.
 * <p>
 * An enabled {@link Transition} may fire. If a {@link Transition} fires, then
 * it consumes one {@link IToken} from each input {@link Place}, and produces
 * one IToken in each output {@link Place}.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id$
 */
public class Transition<D> extends AbstractPetriNetEntity<D> {

	/**
	 * {@link InputArc}s to this Transition.
	 */
	private List<InputArc<?>> inputs;

	/**
	 * {@link OutputArc}s from this Transition.
	 */
	private List<OutputArc<?>> outputs;

	/**
	 * {@link TransitionProcessor} that does the grunt work when this Transition
	 * gets fired.
	 */
	private TransitionProcessor processor;

	/**
	 * 
	 * @param label
	 */
	public Transition(String label) {
		super(label);
	}

	/**
	 * 
	 * @param i
	 */
	public void addInput(InputArc<?> i) {
		if (null == inputs)
			this.inputs = new ArrayList<InputArc<?>>();
		this.inputs.add(i);
	}

	/**
	 * 
	 * @param o
	 */
	public void addOutput(OutputArc<?> o) {
		if (null == outputs)
			this.outputs = new ArrayList<OutputArc<?>>();
		this.outputs.add(o);
	}

	/**
	 * Removes the specified {@link InputArc} connection to this Transition.
	 * 
	 * @param i
	 */
	public void removeInput(InputArc<?> i) {
		if (null != inputs && this.inputs.contains(i))
			this.inputs.remove(i);
	}

	/**
	 * Removes the specified {@link OutputArc} connection from this Transition.
	 * 
	 * @param o
	 */
	public void removeInput(OutputArc<?> o) {
		if (null != outputs && this.outputs.contains(o))
			this.outputs.remove(o);
	}

	/**
	 * @return the inputs
	 */
	public List<InputArc<?>> getInputs() {
		return inputs;
	}

	/**
	 * @return the outputs
	 */
	public List<OutputArc<?>> getOutputs() {
		return outputs;
	}

	/**
	 * Determine if there is atleast one IToken on each of the {@link Place}s
	 * that define this Transition.
	 * 
	 * @return
	 */
	public boolean isEnabled() {
		// Determine if there is atleast one IToken on
		// each of the input Places for this Transition.
		synchronized (this) {
			if (getCurrentState() == State.STATE_RUNNING)
				return false; // Transitions only fire if not running
			List<InputArc<?>> inputsToTransition = getInputs();
			int numInputPlaces = inputsToTransition.size();
			int tokensFound = 0;
			for (InputArc<?> i : inputsToTransition) {
				Place<?> p = i.getSource();
				// for tokens found on each place, increment
				// token counter by 1
				if (p.getTokens().size() > 0)
					tokensFound++;
			}
			if (numInputPlaces == tokensFound)
				return true;
			return false;
		}
	}

	/**
	 * {@link TransitionProcessor} that executes when this Transition gets
	 * fired.
	 * 
	 * @param processor
	 */
	public void setTransitionProcessor(TransitionProcessor processor) {
		this.processor = processor;
	}

	/**
	 * @return the processor
	 */
	public TransitionProcessor getTransitionProcessor() {
		return processor;
	}

	/**
	 * {@link TransitionProcessor} is supposed to be used as an anonymous class.
	 * 
	 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
	 * @since 1.0
	 * @version $Id$
	 */
	public class TransitionProcessor implements IPetriNetEntityProcessor<Transition<D>> {

		@Override
		public void process(Transition<D> entity) throws WorkflowException {
			// TODO Auto-generated method stub
			System.out.println("Processing a Transition with data: " + entity.getData());
		}

	}

}
